/**
 * 
 */
package internal.standard;

import internal.reflect.DefaultInvoker;
import internal.reflect.DefaultTypeConverter;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.aopalliance.intercept.MethodInterceptor;
import org.lightcommons.logger.Log;
import org.lightcommons.logger.LogFactory;
import org.lightcommons.resource.ClassPathResource;
import org.lightcommons.resource.MultiResourceLoader;
import org.lightcommons.template.MultiTemplateFactory;
import org.lightcommons.util.EncodingProperties;
import org.lightcommons.util.PatternMap;
import org.lightcommons.util.StringUtils;
import org.lightmtv.LightMtvException;
import org.lightmtv.config.BeanFactory;
import org.lightmtv.config.Invoker;
import org.lightmtv.config.LifeCycle;
import org.lightmtv.config.MtvConvert;
import org.lightmtv.config.Settings;
import org.lightmtv.config.TypeConverter;
import org.lightmtv.config.UrlConfigEntry;
import org.lightmtv.request.Request;
import org.lightmtv.response.Response;

/**
 * <pre>
 * #url.conf
 * appendsplash=true
 * #direct
 * &circ;(\w+)/xx$-&gt;views.SomeAction.doSome($req,$1,&quot;cmd&quot;,2008-1-1,200)
 * #import
 * &circ;admin/.*$=&gt;classpath:some.settings
 * #redirect
 * &circ;xxx/*--&gt;yyy/$1
 * #rewrite(proxy)
 * &circ;xxx/*==&gt;yyy/$1
 * #layout
 * &circ;xxx/*|&gt;yyy/$1
 * </pre>
 * @author gl
 * @since Jun 30, 2008
 */
public class StandardSettings implements Settings{
	private static final Log log = LogFactory.getLog(StandardSettings.class);
	private static StandardSettings INSTANCE=new StandardSettings();
	private StandardSettings(){
		init();
	};

	/**
	 * @return the iNSTANCE
	 */
	public static StandardSettings getInstance() {
		return INSTANCE;
	}
	
	//================start the main code=====================//
	
	private StandardUrlConfig urlConfig;
	private BeanFactory beanFactory;
	private MultiTemplateFactory templateFactory;
	private MultiResourceLoader templateLoader;
	private EncodingProperties properties;
	private LifeCycle lifeCycle;
	private TypeConverter typeConverter;
	private Invoker invoker;
	private List<MethodInterceptor> methodInterceptors;
	
	/**
	 * 
	 */
	private void init() {
		InputStream is =null;
		try {
			is = new ClassPathResource("url.conf").getInputStream();
			urlConfig = new StandardUrlConfig(is);
		} catch (IOException e) {
			log.error(e);
		} finally{
			if(is!=null)
				try {
					is.close();
				} catch (IOException e) {
				}
		}
		properties = EncodingProperties.loadFromClassPath("org/lightmtv/config/standard.conf","utf-8");
		properties.putAll(EncodingProperties.loadFromClassPath("lightmtv.conf", "utf-8"));
		try {
			beanFactory = (BeanFactory) Class.forName(getProperty("bean.factory")).newInstance();
		} catch (Throwable e) {
			throw new LightMtvException("无法加载bean.factory",e);
		}//new StandardBeanFactory();
		templateFactory = new MultiTemplateFactory();
		templateLoader = new MultiResourceLoader();
		templateFactory.setResourceLoader(templateLoader);

		//typeConvert by properties
		String convertName = getProperty("convert");
		if(StringUtils.hasText(convertName)){
			try {
				typeConverter = (TypeConverter) beanFactory.getBean(convertName);
			}catch (Exception e) {
			}
		}
		if(typeConverter==null){
			typeConverter = new DefaultTypeConverter(getMtvConverts());
		}
		invoker = new DefaultInvoker(typeConverter);
	}
	
	public String getProperty(String name) {
		return properties.getProperty(name);
	}
	
	public PatternMap<UrlConfigEntry> getUrlPatternMap() {
		return urlConfig.getUrlPatternMap();
	}
	
	public BeanFactory getBeanFactory() {
		return beanFactory;
	}
	
	public PatternMap<String> getFilterPatternMap() {
		return urlConfig.getFilterPatternMap();
	}
	
	public MultiTemplateFactory getTemplateFactory() {
		return templateFactory;
	}
	
	public MultiResourceLoader getTemplateLoader() {
		return templateLoader;
	}
	
	public String getEncoding() {
		return "UTF-8";
	}
	
	public LifeCycle getLifeCycle() {
		if(lifeCycle==null){
			String lifeclass = getProperty("lifecycle");
			if(StringUtils.hasText(lifeclass))
				lifeCycle = (LifeCycle) beanFactory.getBean(lifeclass);
			else{
				lifeCycle = new LifeCycle(){
					public void afterResponse(Response response) {
					}
					public void beforeRequest(Request request) {
					}
					public void start() {
					}
					public void stop() {
					}
				};
			}
		}
		return lifeCycle;
	}
	
	private List<MtvConvert> getMtvConverts(){
		List<MtvConvert> converts = new ArrayList<MtvConvert>();
		String convertsDefine = getProperty("convert");
		if(StringUtils.hasText(convertsDefine)){
			String[] convertStrs=convertsDefine.split(",");
			for(String convertStr: convertStrs){
				if(StringUtils.hasText(convertStr)){
					converts.add((MtvConvert) beanFactory.getBean(convertStr.trim()));
				}
			}
		}
		return converts;
	}
	
	public TypeConverter getTypeConverter() {
		return typeConverter;
	}
	
	public Invoker getInvoker() {
		return invoker;
	}
	
	/*
	 * 获取Interceptors
	 * (non-Javadoc)
	 * @see org.lightmtv.config.Settings#getInterceptors()
	 */
	public List<MethodInterceptor> getMethodInterceptors() {
		if(methodInterceptors==null){
			Pattern interceptorPattern =Pattern.compile("^interceptor\\.(\\d+)$");
			methodInterceptors = new ArrayList<MethodInterceptor>();
			for(Map.Entry e : properties.entrySet()){
				String key = (String) e.getKey();
				Matcher matcher = interceptorPattern.matcher(key.trim());
				if(matcher.matches()){
					try{
						String value = matcher.group(1);
						int index = Integer.valueOf(value);
						if(index>255){
							log.warn(key +" the index is too large,max 255");
							index=255;
						}
						
						MethodInterceptor interceptor = (MethodInterceptor) getBeanFactory().getBean(((String) e.getValue()).trim());
						setElement(methodInterceptors, interceptor, index);
					}catch(Exception x){
						log.error("error for '"+key+"="+e.getValue(),x);
					}
				}
			}
			for(Iterator<MethodInterceptor> i=methodInterceptors.iterator();i.hasNext();){
				MethodInterceptor it = i.next();
				if(it==null)
					i.remove();
			}
		}
		return methodInterceptors;
	}
	
	/**
	 * Insert object at specified location
	 * @param collection
	 * @param value
	 * @param index
	 */
	private static void setElement(List list,Object element,int index){
		if(index>=list.size()){
			for(int i=(index-list.size());i>=0;i--)
				list.add(null);
		}
		if(list.get(index)!=null){
			list.add(index, element);
		}else{
			list.set(index, element);
		}
	}
}
